<?php

namespace IYUU\sdk\CoolQ;

use RedisQueue;
use SplQueue;

/**
 * OneBot的API
 * - See: https://github.com/botuniverse/onebot
 * - See: https://docs.go-cqhttp.org/
 * - OneBot 是一个聊天机器人应用接口标准，旨在统一不同聊天平台上的机器人应用开发接口，使开发者只需编写一次代码即可应用到多种机器人平台。
 * Class OneBotApi
 * @package IYUU\sdk\CoolQ
 */
class OneBotApi
{
    // 发送私聊消息
    const send_private_msg = '/send_private_msg';

    // 发送群消息
    const send_group_msg = '/send_group_msg';

    // 发送合并转发 ( 群 )
    const send_group_forward_msg = '/send_group_forward_msg';

    // 发送消息
    const send_msg = '/send_msg';

    // 撤回消息
    const delete_msg = '/delete_msg';

    // 获取消息
    const get_msg = '/get_msg';

    // 获取合并转发内容
    const get_forward_msg = '/get_forward_msg';

    // 获取图片消息
    const get_image = '/get_image';

    // 群组踢人
    const set_group_kick = '/set_group_kick';

    // 群组单人禁言
    const set_group_ban = '/set_group_ban';

    // 群组匿名用户禁言
    const set_group_anonymous_ban = '/set_group_anonymous_ban';

    // 群组全员禁言
    const set_group_whole_ban = '/set_group_whole_ban';

    // 群组设置管理员
    const set_group_admin = '/set_group_admin';

    // 群组匿名(该 API 暂未被 go-cqhttp 支持 2021年8月3日)
    const set_group_anonymous = '/set_group_anonymous';

    // 设置群名片 ( 群备注 )
    const set_group_card = '/set_group_card';

    // 设置群名
    const set_group_name = '/set_group_name';

    // 退出群组
    const set_group_leave = '/set_group_leave';

    // 设置群组专属头衔
    const set_group_special_title = '/set_group_special_title';

    // 处理加好友请求
    const set_friend_add_request = '/set_friend_add_request';

    // 处理加群请求／邀请
    const set_group_add_request = '/set_group_add_request';

    // 获取登录号信息
    const get_login_info = '/get_login_info';

    // 获取企点账号信息
    const qidian_get_account_info = '/qidian_get_account_info';

    // 获取陌生人信息
    const get_stranger_info = '/get_stranger_info';

    // 获取好友列表
    const get_friend_list = '/get_friend_list';

    // 删除好友
    const delete_friend = '/delete_friend';

    // 获取群信息
    const get_group_info = '/get_group_info';

    // 获取群列表
    const get_group_list = '/get_group_list';

    // 获取群成员信息
    const get_group_member_info = '/get_group_member_info';

    // 获取群成员列表
    const get_group_member_list = '/get_group_member_list';

    // 获取群荣誉信息
    const get_group_honor_info = '/get_group_honor_info';

    // 获取 Cookies(该 API 暂未被 go-cqhttp 支持 2021年8月3日)
    const get_cookies = '/get_cookies';

    // 获取 CSRF Token(该 API 暂未被 go-cqhttp 支持 2021年8月3日)
    const get_csrf_token = '/get_csrf_token';

    // 获取 QQ 相关接口凭证,即上面两个接口的合并(该 API 暂未被 go-cqhttp 支持 2021年8月3日)
    const get_credentials = '/get_credentials';

    // 获取语音(该 API 暂未被 go-cqhttp 支持 2021年8月3日)
    const get_record = '/get_record';

    // 检查是否可以发送图片
    const can_send_image = '/can_send_image';

    // 检查是否可以发送语音
    const can_send_record = '/can_send_record';

    // 获取版本信息
    const get_version_info = '/get_version_info';

    // 重启 go-cqhttp
    const set_restart = '/set_restart';

    // 清理缓存(该 API 暂未被 go-cqhttp 支持 2021年8月3日)
    const clean_cache = '/clean_cache';

    // 设置群头像
    const set_group_portrait = '/set_group_portrait';

    // 获取群系统消息
    const get_group_system_msg = '/get_group_system_msg';

    // 上传群文件
    const upload_group_file = '/upload_group_file';

    // 获取群文件系统信息
    const get_group_file_system_info = '/get_group_file_system_info';

    // 获取群根目录文件列表
    const get_group_root_files = '/get_group_root_files';

    // 获取群子目录文件列表
    const get_group_files_by_folder = '/get_group_files_by_folder';

    // 获取群文件资源链接
    const get_group_file_url = '/get_group_file_url';

    // 获取状态
    const get_status = '/get_status';

    // 获取群 @全体成员 剩余次数
    const get_group_at_all_remain = '/get_group_at_all_remain';

    // 对事件执行快速操作 ( 隐藏 API )
    const handle_quick_operation = '/.handle_quick_operation';

    // 获取VIP信息
    const _get_vip_info = '/_get_vip_info';

    // 发送群公告
    const _send_group_notice = '/_send_group_notice';

    // 重载事件过滤器
    const reload_event_filter = '/reload_event_filter';

    // 下载文件到缓存目录
    const download_file = '/download_file';

    // 获取当前账号在线客户端列表
    const get_online_clients = '/get_online_clients';

    // 获取群消息历史记录
    const get_group_msg_history = '/get_group_msg_history';

    // 设置精华消息
    const set_essence_msg = '/set_essence_msg';

    // 移出精华消息
    const delete_essence_msg = '/delete_essence_msg';

    // 获取精华消息列表
    const get_essence_msg_list = '/get_essence_msg_list';

    // 检查链接安全性
    const check_url_safely = '/check_url_safely';

    // 获取在线机型
    const _get_model_show = '/_get_model_show';

    // 设置在线机型
    const _set_model_show = '/_set_model_show';

    /**
     * 主机
     * @var string
     */
    private $host;

    /**
     * Token
     * @var string
     */
    private $token;

    /**
     * 队列（主动发送的消息）
     * @var SplQueue
     */
    private $queue;

    /**
     * 构造函数
     * CoolQApi constructor.
     * @param string $host
     * @param string $token
     */
    public function __construct(string $host = '127.0.0.1:5700', string $token = '')
    {
        $this->host = $host;
        $this->token = $token;
        // 队列
        $this->queue = new SplQueue();
        // 注册一个会在php中止时执行的函数，发送所有队列内的消息
        register_shutdown_function(array($this, 'sendAllMessage'));
    }

    /**
     * 取出队列头部的成员
     *
     * @note dequeue方法等效于父类shift方法
     * @see splDoublyLinkedList::shift()
     * @return Message
     */
    public function dequeue(): Message
    {
        return $this->queue->dequeue();
    }

    /**
     * 往队列尾部添加成员
     * @note dequeue方法等效于父类push方法
     * @param Message $message
     * @see splDoublyLinkedList::push()
     */
    public function enqueue(Message $message)
    {
        $this->queue->enqueue($message);
    }

    /**
     * 发送所有队列内的消息
     */
    public function sendAllMessage()
    {
        while (!$this->queue->isEmpty()) {
            $this->send($this->dequeue());
        }
    }

    /**
     * 发送一条消息
     * @param Message $message
     */
    public function send(Message $message)
    {
        if ($message->toGroup) {
            $result = $this->sendGroupMsg($message->id, $message->msg, $message->auto_escape);
        } else {
            $result = $this->sendPrivateMsg($message->id, $message->msg, $message->auto_escape);
        }
        // 需要延迟撤回的消息
        if ($message->expire && !empty($result->message_id)) {
            $message_id = $result->message_id;
            $body = [
                'action' => 'delete_msg',
                'message_id' => $message_id
            ];
            //屏蔽消息撤回
            //RedisQueue::add('go-cqhttp:delete_msg', $message_id, $message->expire, getConfig('callback'), $body);
        }
    }

    /**
     * 发送私聊消息
     * @param $user_id
     * @param $message
     * @param bool $auto_escape
     * @return mixed
     */
    public function sendPrivateMsg($user_id, $message, bool $auto_escape = false)
    {
        $api = self::send_private_msg;
        $param = [
            'user_id' => $user_id,
            'message' => $message,
            'auto_escape' => $auto_escape,
        ];
        return $this->query($api, $param);
    }

    /**
     * 发送群消息
     * @param $group_id
     * @param $message
     * @param bool $auto_escape
     * @return mixed
     */
    public function sendGroupMsg($group_id, $message, bool $auto_escape = false)
    {
        $api = self::send_group_msg;
        $param = [
            'group_id' => $group_id,
            'message' => $message,
            'auto_escape' => $auto_escape,
        ];
        return $this->query($api, $param);
    }

    /**
     * 发送消息
     * @param $message_type
     * @param $id
     * @param $message
     * @param bool $auto_escape
     * @return mixed
     */
    public function sendMsg($message_type, $id, $message, bool $auto_escape = false)
    {
        $api = self::send_msg;
        $param = [
            'message_type' => $message_type,
            'user_id' => $id,
            'group_id' => $id,
            'message' => $message,
            'auto_escape' => $auto_escape,
        ];
        // 移除多余键
        if ($message_type === 'group') {
            unset($param['user_id']);
        } else {
            unset($param['group_id']);
        }
        return $this->query($api, $param);
    }

    public function deleteMsg($message_id)
    {
        $api = self::delete_msg;
        $param = [
            'message_id' => $message_id,
        ];
        return $this->query($api, $param);
    }

    public function setGroupKick($group_id, $user_id, $reject_add_request = false)
    {
        $api = self::set_group_kick;
        $param = [
            'group_id' => $group_id,
            'user_id' => $user_id,
            'reject_add_request' => $reject_add_request,
        ];
        return $this->query($api, $param);
    }

    public function setGroupBan($group_id, $user_id, $duration = 30 * 60)
    {
        $api = self::set_group_ban;
        $param = [
            'group_id' => $group_id,
            'user_id' => $user_id,
            'duration' => $duration,
        ];
        return $this->query($api, $param);
    }

    public function setGroupAnonymousBan($group_id, $flag, $duration = 30 * 60)
    {
        $api = self::set_group_anonymous_ban;
        $param = [
            'group_id' => $group_id,
            'flag' => $flag,
            'duration' => $duration,
        ];
        return $this->query($api, $param);
    }

    public function setGroupWholeBan($group_id, $enable = true)
    {
        $api = self::set_group_whole_ban;
        $param = [
            'group_id' => $group_id,
            'enable' => $enable,
        ];
        return $this->query($api, $param);
    }

    public function setGroupAdmin($group_id, $user_id, $enable = true)
    {
        $api = self::set_group_admin;
        $param = [
            'group_id' => $group_id,
            'user_id' => $user_id,
            'enable' => $enable,
        ];
        return $this->query($api, $param);
    }

    public function setGroupAnonymous($group_id, $enable = true)
    {
        $api = self::set_group_anonymous;
        $param = [
            'group_id' => $group_id,
            'enable' => $enable,
        ];
        return $this->query($api, $param);
    }

    public function setGroupCard($group_id, $user_id, $card = null)
    {
        $api = self::set_group_card;
        $param = [
            'group_id' => $group_id,
            'user_id' => $user_id,
            'card' => $card,
        ];
        return $this->query($api, $param);
    }

    public function setGroupLeave($group_id, $is_dismiss = false)
    {
        $api = self::set_group_leave;
        $param = [
            'group_id' => $group_id,
            'is_dismiss' => $is_dismiss,
        ];
        return $this->query($api, $param);
    }

    public function setGroupSpecialTitle($group_id, $user_id, $special_title = null, $duration = -1)
    {
        $api = self::set_group_special_title;
        $param = [
            'group_id' => $group_id,
            'user_id' => $user_id,
            'special_title' => $special_title,
            'duration' => $duration,
        ];
        return $this->query($api, $param);
    }

    public function setFriendAddRequest($flag, $approve = true, $remark = '')
    {
        $api = self::set_friend_add_request;
        $param = [
            'flag' => $flag,
            'approve' => $approve,
            'remark' => $remark,
        ];
        return $this->query($api, $param);
    }

    public function setGroupAddRequest($flag, $type, $approve = true, $reason = '')
    {
        $api = self::set_group_add_request;
        $param = [
            'flag' => $flag,
            'type' => $type,
            'approve' => $approve,
            'reason' => $reason,
        ];
        return $this->query($api, $param);
    }

    public function getLoginInfo()
    {
        $api = self::get_login_info;
        $param = [];
        return $this->query($api, $param);
    }

    public function getStrangerInfo($user_id, $no_cache = false)
    {
        $api = self::get_stranger_info;
        $param = [
            'user_id' => $user_id,
            'no_cache' => $no_cache,
        ];
        return $this->query($api, $param);
    }

    public function getGroupList()
    {
        $api = self::get_group_list;
        $param = [];
        return $this->query($api, $param);
    }

    public function getGroupMemberInfo($group_id, $user_id, $no_cache = false)
    {
        $api = self::get_group_member_info;
        $param = [
            'group_id' => $group_id,
            'user_id' => $user_id,
            'no_cache' => $no_cache,
        ];
        return $this->query($api, $param);
    }

    public function getGroupMemberList($group_id)
    {
        $api = self::get_group_member_list;
        $param = [
            'group_id' => $group_id,
        ];
        return $this->query($api, $param);
    }

    public function getCookies()
    {
        $api = self::get_cookies;
        $param = [];
        return $this->query($api, $param);
    }

    public function getCsrfToken()
    {
        $api = self::get_csrf_token;
        $param = [];
        return $this->query($api, $param);
    }

    public function getCredentials()
    {
        $api = self::get_credentials;
        $param = [];
        return $this->query($api, $param);
    }

    public function getRecord($file, $out_format)
    {
        $api = self::get_record;
        $param = [
            'file' => $file,
            'out_format' => $out_format,
        ];
        return $this->query($api, $param);
    }

    /**
     * 获取go-cqhttp运行状态的文本信息
     * @return mixed
     */
    public function getStatus()
    {
        $api = self::get_status;
        $param = [];
        return $this->query($api, $param);
    }

    /**
     * 获取版本信息
     * @return mixed
     */
    public function getVersionInfo()
    {
        $api = self::get_version_info;
        $param = [];
        return $this->query($api, $param);
    }

    public function setRestart($delay = 2000)
    {
        $api = self::set_restart;
        $param = [
            'delay' => $delay
        ];
        return $this->query($api, $param);
    }

    public function getFriendList($flat = false)
    {
        $api = self::get_friend_list;

        return $this->query($api);
    }

    public function getGroupInfo($group_id)
    {
        $api = self::get_group_info;
        $param = [
            'group_id' => $group_id,
        ];
        return $this->query($api, $param);
    }

    public function _get_vip_info($user_id)
    {
        $api = self::_get_vip_info;
        $param = [
            'user_id' => $user_id,
        ];
        return $this->query($api, $param);
    }

    /**
     * 获取go-cqhttp运行状态(转换为纯文本)
     * @return string
     */
    public function getStatusText(): string
    {
        $result = $this->getStatus();

        if (empty($result->stat)) {
            return '获取失败，请稍后重试';
        }
        $stat = $result->stat;
        $rs = '在线状态：' . (!empty($result->online) ? '在线' : '离线') . PHP_EOL;
        $rs .= '收到包总数：' . ($stat->packet_received ?? 0) . PHP_EOL;
        $rs .= '发送包总数：' . ($stat->packet_sent ?? 0) . PHP_EOL;
        $rs .= '丢失：' . ($stat->packet_lost ?? 0) . PHP_EOL;
        $rs .= '接受信息总数：' . ($stat->message_received ?? 0) . PHP_EOL;
        $rs .= '发送信息总数：' . ($stat->message_sent ?? 0) . PHP_EOL;
        $rs .= 'TCP链接断开次数：' . ($stat->disconnect_times ?? 0) . PHP_EOL;
        $rs .= '账号掉线次数：' . ($stat->lost_times ?? 0) . PHP_EOL;
        $rs .= '最后发送：' . ($stat->last_message_time ? date('Y-m-d H:i:s', $stat->last_message_time) : 0);
        return $rs;
    }

    /**
     * 获取版本信息(转换为纯文本)
     * @return string
     */
    public function getVersionInfoText(): string
    {
        $data = $this->getVersionInfo();
        if ($data) {
            $rs = '[CQ:face,id=54]框架部分' . PHP_EOL;
            $rs .= '应用标识：' . ($data->app_name ?? 0) . PHP_EOL;
            $rs .= '应用版本：' . ($data->app_version ?? 0) . PHP_EOL;
            //$rs .= '应用完整名称：' . ($data->app_full_name ?? 0) . PHP_EOL;
            $rs .= 'OneBot标准版本：' . ($data->protocol_version ?? 0) . PHP_EOL;
            $rs .= '运行时：' . ($data->runtime_os ?? 0) . ' / ' . ($data->runtime_version ?? 0) . PHP_EOL . PHP_EOL;
            $rs .= '[CQ:face,id=69]接口部分' . PHP_EOL;
            $rs .= '最后更新：' . get_current_git_fileMtime() . PHP_EOL;
            $rs .= 'PHP版本：v' . (PHP_VERSION ?? 0);
            return $rs;
        }

        return '获取getVersionInfo失败';
    }

    /**
     * @param string $api
     * @param array $param
     * @return mixed
     */
    private function query(string $api, array $param = [])
    {
        $queryStr = '?';
        if (!empty($this->token)) {
            $param['access_token'] = $this->token;      //追加access_token到参数表
        }
        foreach ($param as $key => $value) {
            $queryStr .= ($key . '=' . urlencode(is_bool($value) ? (int)$value : $value) . '&');
        }
        $result = json_decode(file_get_contents('http://' . $this->host . $api . $queryStr));

        if (isset($result->retcode)) {
            switch ($result->retcode) {
                case 0:
                    return $result->data ?? null;
                default:
                    return null;
            }
        }

        return null;
    }
}
